use model_graph_types::{
    connector::{Connector, ExecuteOptions},
    database::DataSource,
    modeling::{
        Class, ClassAttribute, ClassPersistenceGetter, ClassRelation, ClassTraits,
        DataSourceClassAttributeMapping, DataSourceClassMapping, DataSourceClassRelationMapping,
        DataSourceEntity, Domain, DomainDependency, NameSpaceClassRelation, Relation, Traits,
        Value,
    },
    transform::{
        DataTransformOptions, DataTransformUnit, DomainTransformerOptions, DomainTransformerUnit,
    },
};
use sqlite_connector::SqliteConnector;

/// 域数据转移
pub async fn domain_transform(
    source: &DataSource,
    target: &DataSource,
    domain: &DomainTransformerUnit,
    options: &DomainTransformerOptions,
) -> anyhow::Result<()> {
    //
    let mut units: Vec<DataTransformUnit> = vec![];

    if options.ddl {
        units.push(DataTransformUnit::structs(&vec![
            Domain::table_name()?,
            DomainDependency::table_name()?,
            Class::table_name()?,
            Relation::table_name()?,
            Traits::table_name()?,
            ClassAttribute::table_name()?,
            ClassRelation::table_name()?,
            ClassTraits::table_name()?,
        ])?);
    }

    units.append(&mut vec![
        DataTransformUnit::values(
            &Domain::table_name()?,
            &format!(" where identifier = '{}' ", domain.domain_identifier),
        )?,
        DataTransformUnit::values(
            &Class::table_name()?,
            &format!(" where domain_identifier = '{}' ", domain.domain_identifier),
        )?,
        DataTransformUnit::values(
            &Relation::table_name()?,
            &format!(" where domain_identifier = '{}' ", domain.domain_identifier),
        )?,
        DataTransformUnit::values(
            &Traits::table_name()?,
            &format!(" where domain_identifier = '{}' ", domain.domain_identifier),
        )?,
        DataTransformUnit::values(
            &DomainDependency::table_name()?,
            &format!(" where domain_identifier = '{}' ", domain.domain_identifier),
        )?,
        DataTransformUnit::sql(
            &format!("select DISTINCT p.namespace_identifier, p.class_identifier, p.options, p.description from {} p, _g01_meta_namespaces o where o.identifier =  p.namespace_identifier and o.domain_identifier = '{}'", NameSpaceClassRelation::table_name()?, domain.domain_identifier),
            &format!(" insert into {}(namespace_identifier, class_identifier, options, description) values($1,$2,$3,$4) ", NameSpaceClassRelation::table_name()?,),
        )?,
        DataTransformUnit::sql(
            &format!("select DISTINCT p.class_identifier, p.identifier, p.name, p.title, p.is_key, p.data_type, p.collection_type, p.value_size, p.is_number, p.is_autoincrement, p.unit, p.default_value, p.is_nullable, p.position, p.scope,  p.comment, p.options,  p.description from _g01_meta_class_attributes p, _g01_meta_classes o where o.identifier =  p.class_identifier and o.domain_identifier = '{}'", domain.domain_identifier),
            &format!(" insert into _g01_meta_class_attributes(class_identifier, identifier, name, title, is_key, data_type, collection_type, value_size, is_number, is_autoincrement, unit, default_value, is_nullable, position, scope, comment, options, description) values($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18) "),
        )?,
        DataTransformUnit::sql(
            &format!("select DISTINCT p.relation_identifier, p.src_identifier, p.target_identifier,p.src_count, p.target_count,p.related_type, p.related_at, p.comment, p.options,  p.description from {} p, _g01_meta_relations o where o.identifier =  p.relation_identifier and o.domain_identifier = '{}'", ClassRelation::table_name()?, domain.domain_identifier),
            &format!(" insert into {}(relation_identifier, src_identifier,target_identifier,src_count,target_count,related_type,related_at,comment, options, description) values($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) ", ClassRelation::table_name()?,),
        )?,
        DataTransformUnit::sql(
            &format!("select DISTINCT p.class_identifier, p.identifier, p.name,p.title, p.icon,p.trait_type, p.path, p.comment, p.options,  p.description from {} p, _g01_meta_classes o where o.identifier =  p.class_identifier and o.domain_identifier = '{}'", ClassTraits::table_name()?, domain.domain_identifier),
            &format!(" insert into {}(class_identifier, identifier,name,title,icon,trait_type,path,comment, options, description) values($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) ", ClassTraits::table_name()?,),
        )?,
    ]);

    let ds_options = DataTransformOptions::new_with_ignore(options.ignore_errs, true);
    //
    let result = crate::sqlite::transform(source, target, units, ds_options).await;

    result
}

/// 清空领域
pub async fn clear_domain(
    meta_datasource: &DataSource,
    domains: Vec<String>,
) -> anyhow::Result<()> {
    let target_connector = SqliteConnector::open(&meta_datasource).await?;
    for domain_identifier in domains {
        target_connector
        .execute(&format!("delete from _g01_meta_datasource_class_attribute_mappings where id in (select DISTINCT p.id from _g01_meta_classes o,_g01_meta_datasource_class_attribute_mappings p where o.identifier = p.class_identifier and o.domain_identifier = $1)", ), &vec![Value::String(domain_identifier.clone())], ExecuteOptions::default())
        .await?;
        target_connector
            .execute(
                &format!(
                    "delete from _g01_meta_datasource_class_mappings where domain_identifier = $1",
                ),
                &vec![Value::String(domain_identifier.clone())],
                ExecuteOptions::default(),
            )
            .await?;
        target_connector
        .execute(&format!("delete from _g01_meta_class_relations where id in (select DISTINCT p.id from _g01_meta_classes o, _g01_meta_class_relations p where (o.identifier =  p.src_identifier or o.identifier = p.target_identifier) and o.domain_identifier = $1)", ), &vec![Value::String(domain_identifier.clone())], ExecuteOptions::default())
        .await?;
        target_connector
        .execute(&format!("delete from _g01_meta_class_attributes where id in (select DISTINCT p.id from _g01_meta_classes o, _g01_meta_class_attributes p where o.identifier = p.class_identifier and o.domain_identifier = $1) ", ), &vec![Value::String(domain_identifier.clone())], ExecuteOptions::default())
        .await?;
        target_connector
            .execute(
                &format!("delete from _g01_meta_namespaces where domain_identifier = $1 ",),
                &vec![Value::String(domain_identifier.clone())],
                ExecuteOptions::default(),
            )
            .await?;
        target_connector
            .execute(
                &format!("delete from _g01_meta_traits where domain_identifier = $1 ",),
                &vec![Value::String(domain_identifier.clone())],
                ExecuteOptions::default(),
            )
            .await?;
        target_connector
            .execute(
                &format!("delete from _g01_meta_relations where domain_identifier = $1 ",),
                &vec![Value::String(domain_identifier.clone())],
                ExecuteOptions::default(),
            )
            .await?;
        target_connector
            .execute(
                &format!("delete from _g01_meta_classes where domain_identifier = $1 ",),
                &vec![Value::String(domain_identifier.clone())],
                ExecuteOptions::default(),
            )
            .await?;
    }

    Ok(())
}

/// 删除领域
pub async fn delete_domain(
    meta_datasource: &DataSource,
    domains: Vec<String>,
) -> anyhow::Result<()> {
    clear_domain(meta_datasource, domains.clone()).await?;
    for domain_identifier in domains {
        let target_connector = SqliteConnector::open(&meta_datasource).await?;
        target_connector
            .execute(
                &format!("delete from _g01_meta_domains where identifier = $1 ",),
                &vec![Value::String(domain_identifier.clone())],
                ExecuteOptions::default(),
            )
            .await?;
    }

    Ok(())
}
